package org.truxingame.input;

import org.truxingame.input.xinput.XINPUT_CAPABILITIES;
import org.truxingame.input.xinput.XINPUT_STATE;
import org.truxingame.input.xinput.XINPUT_VIBRATION;
import org.truxingame.input.xinput.XInput;
import org.truxingame.PlayerIndex;
import org.truxingame.Vector2;

public class GamePad {
	public static GamePadState getState(PlayerIndex i) {
		return getState(i, GamePadDeadZone.IndependentAxes);
	}
	public static GamePadState getState(PlayerIndex i, GamePadDeadZone deadZone) {
		XINPUT_STATE state = new XINPUT_STATE(); 
		long err = XInput.getState(i.ordinal(), state);
		Buttons buttons = new Buttons(state.getGamepad().getWButtons());

		if (state.getGamepad().getBLeftTrigger() > XInput.XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
			buttons.or(Buttons.LeftTrigger);
		
		if (state.getGamepad().getBRightTrigger() > XInput.XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
			buttons.or(Buttons.RightTrigger);


		if (err != 0) return GamePadState.getDisconnected();

		int deadLeft = XInput.XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE;
		int deadRight = XInput.XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE;
		
		
		float left = clamp(state.getGamepad().getBLeftTrigger(),255, 0);
		float right = clamp(state.getGamepad().getBRightTrigger(),255, 0);
		

		/// Add Virtual analog buttons and clamp thimbsticks
		int iX = state.getGamepad().getSThumbLX();
		int iY = state.getGamepad().getSThumbLY();
		
		// Buttons use default dead zone
		if (iX > (32767-deadLeft)  ) buttons.or(Buttons.LeftStickRight);
		if (iX < (deadLeft-32768) ) buttons.or(Buttons.LeftStickLeft);
		if (iY > (32767-deadLeft) ) buttons.or(Buttons.LeftStickUp);
		if (iY < (deadLeft-32768)) buttons.or(Buttons.LeftStickDown);
		
		if (deadZone == GamePadDeadZone.None) deadLeft = 0;
		float x = clamp(iX, 32767, deadLeft);
		float y = clamp(iY, 32767, deadLeft );

		if (deadZone == GamePadDeadZone.Circular) {
			if (x==0.0) y= 0;
			else if (y==0.0) x= 0;
		}
		
		Vector2 leftT = new Vector2(x,y);
			
		iX = state.getGamepad().getSThumbRX();
		iY = state.getGamepad().getSThumbRY();
		
		
		if (iX > (32767-deadRight)  ) buttons.or(Buttons.RightStickRight);
		if (iX < (deadRight-32768) ) buttons.or(Buttons.RightStickLeft);
		if (iY > (32767-deadRight) ) buttons.or(Buttons.RightStickUp);
		if (iY < (deadRight-32768)) buttons.or(Buttons.RightStickDown);

		if (deadZone == GamePadDeadZone.None) deadRight = 0;

		x = clamp(iX, 32767, deadRight);
		y = clamp(iY, 32767, deadRight);

		if (deadZone == GamePadDeadZone.Circular) {
			if (x==0.0) y= 0;
			else if (y==0.0) x= 0;
		}
		Vector2 rightT = new Vector2(x,y);
		
		
		GamePadState ret = new GamePadState(
				new GamePadButtons(buttons), 
				new GamePadDPad(buttons), 
				new GamePadTriggers(left, right), 
				new GamePadThumbSticks(leftT, rightT));

		ret.packet = state.getDwPacketNumber();

		return ret;
	}

	public static boolean SetVibration (
			PlayerIndex playerIndex, float leftMotor, float rightMotor) {

		XINPUT_VIBRATION vibe = new XINPUT_VIBRATION();
		vibe.setWLeftMotorSpeed((int)(leftMotor*65535 ));
		vibe.setWRightMotorSpeed((int)(rightMotor*65535 ));
		if (XInput.setState(playerIndex.ordinal(), vibe) == 0)
			return true;

		return false;
	}

	private static float clamp(int s, int div, int dead) {
		if (s>=0) {
			if (s < dead) s = 0;
			else if (s+dead >= div) s = div;
		} else {
			if (s > -dead) s = 0;
			else if (s-dead <= -div) s = -div;

		}
		return ((float)s) / ((float)div);
	}
	
	
	public static GamePadCapabilities GetCapabilities(PlayerIndex playerIndex) {
		XINPUT_CAPABILITIES caps = new XINPUT_CAPABILITIES ();
		GamePadCapabilities gbcaps = new GamePadCapabilities();
		
		if (XInput.getCapabilities(playerIndex.ordinal(), XInput.XINPUT_FLAG_GAMEPAD, caps) == 0)
			gbcaps.connected = true;
		else 
			gbcaps.connected = false;

		gbcaps.type = GamePadType.Unknown;
		if (caps.getSubType() == XInput.XINPUT_DEVSUBTYPE_GAMEPAD)
			gbcaps.type = GamePadType.GamePad;
		else if (caps.getSubType() == XInput.XINPUT_DEVSUBTYPE_ARCADE_STICK)
			gbcaps.type = GamePadType.ArcadeStick;
		else if (caps.getSubType() == XInput.XINPUT_DEVSUBTYPE_DANCE_PAD)
			gbcaps.type = GamePadType.DancePad;
		else if (caps.getSubType() == XInput.XINPUT_DEVSUBTYPE_DRUM_KIT)
			gbcaps.type = GamePadType.DrumKit;
		else if (caps.getSubType() == XInput.XINPUT_DEVSUBTYPE_FLIGHT_SICK)
			gbcaps.type = GamePadType.FlightStick;
		else if (caps.getSubType() == XInput.XINPUT_DEVSUBTYPE_GUITAR)
			gbcaps.type = GamePadType.Guitar;
		else if (caps.getSubType() == XInput.XINPUT_DEVSUBTYPE_WHEEL)
			gbcaps.type = GamePadType.Wheel;

		
		gbcaps.buttons = new Buttons(caps.getGamepad().getWButtons());
		
		gbcaps.leftT = caps.getGamepad().getBLeftTrigger()!=0;
		gbcaps.rightT = caps.getGamepad().getBRightTrigger()!=0;
		
		
		gbcaps.leftX = caps.getGamepad().getSThumbLX()!=0;
		gbcaps.leftY = caps.getGamepad().getSThumbLY()!=0;
		
		gbcaps.rightX = caps.getGamepad().getSThumbRX()!=0;
		gbcaps.rightY = caps.getGamepad().getSThumbRY()!=0;
		
		gbcaps.leftMotor = caps.getVibration().getWLeftMotorSpeed() != 0;
		gbcaps.rightMotor = caps.getVibration().getWRightMotorSpeed() != 0;
		
		gbcaps.voice = (caps.getFlags() & XInput.XINPUT_CAPS_VOICE_SUPPORTED) 
				== XInput.XINPUT_CAPS_VOICE_SUPPORTED;
		
		return gbcaps;
	}

}
